package drds.plus.sql_process.rule;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import drds.plus.common.lifecycle.AbstractLifecycle;
import drds.plus.common.model.Application;
import drds.plus.rule_engine.rule_calculate.DataNodeDataScatterInfo;
import drds.plus.sql_process.optimizer.cost_esitimater.statistics.*;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;

import java.util.concurrent.ExecutionException;

/**
 * 基于Rule获取到物理的group进行查找
 */
@Slf4j
public class RuleStatisticsManager extends AbstractLifecycle implements StatisticsManager {
    @Override
    public Logger log() {
        return log;
    }

    private RouteOptimizer routeOptimizer;
    private Application application;
    private LocalStatisticsManager localStatisticsManager;
    private boolean useCache;
    private LoadingCache<drds.plus.common.model.DataNode, RepositoryStatisticsManager> groupToRepositoryStatisticsManagerCache = null;

    public RuleStatisticsManager(RouteOptimizer routeOptimizer, Application application) {
        this.routeOptimizer = routeOptimizer;
        this.application = application;
    }

    protected void doInit() {
        super.doInit();
        groupToRepositoryStatisticsManagerCache = CacheBuilder.newBuilder().build(new CacheLoader<drds.plus.common.model.DataNode, RepositoryStatisticsManager>() {

            public RepositoryStatisticsManager load(drds.plus.common.model.DataNode dataNode) throws Exception {
                RepositoryStatisticsManager repositoryStatisticsManager = new RepositoryStatisticsManager();
                repositoryStatisticsManager.setDataNode(dataNode);
                repositoryStatisticsManager.setLocalStatisticsManager(localStatisticsManager);
                repositoryStatisticsManager.setUseCache(useCache);
                repositoryStatisticsManager.init();
                return repositoryStatisticsManager;
            }
        });
    }

    protected void doDestroy() {
        super.doDestroy();

        for (RepositoryStatisticsManager repositoryStatisticsManager : groupToRepositoryStatisticsManagerCache.asMap().values()) {
            repositoryStatisticsManager.destroy();
        }
    }

    public TableStatistics getTableStatistics(String tableName) {
        DataNodeDataScatterInfo dataNodeDataScatterInfo = routeOptimizer.shardAny(tableName);
        if (dataNodeDataScatterInfo.getDataNodeId() == null) {
            // 没有对应的规则，也没有default datanode，则可能是一个不存在的表
            // 尝试找一下local
            return localStatisticsManager.getTableStatistics(tableName);
        } else {
            drds.plus.common.model.DataNode group = application.getDataNode(dataNodeDataScatterInfo.getDataNodeId()); // 先找到group
            try {
                return groupToRepositoryStatisticsManagerCache.get(group).getTableStatistics(dataNodeDataScatterInfo.getTableNameSet().iterator().next());
            } catch (ExecutionException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public TableIndexStatisticsItem getIndexStatistics(String indexName) {
        DataNodeDataScatterInfo dataNodeDataScatterInfo = routeOptimizer.shardAny(indexName);
        if (dataNodeDataScatterInfo.getDataNodeId() == null) {
            // 没有对应的规则，也没有default datanode，则可能是一个不存在的表
            // 尝试找一下local
            return localStatisticsManager.getIndexStatistics(indexName);
        } else {
            drds.plus.common.model.DataNode group = application.getDataNode(dataNodeDataScatterInfo.getDataNodeId()); // 先找到group
            if (group == null) {
                throw new RuntimeException("not found groupName : " + dataNodeDataScatterInfo.getDataNodeId());
            }
            try {
                return groupToRepositoryStatisticsManagerCache.get(group).getIndexStatistics(dataNodeDataScatterInfo.getTableNameSet().iterator().next());
            } catch (ExecutionException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public void setRouteOptimizer(RouteOptimizer routeOptimizer) {
        this.routeOptimizer = routeOptimizer;
    }

    public void setLocalStatisticsManager(LocalStatisticsManager localStatisticsManager) {
        this.localStatisticsManager = localStatisticsManager;
    }

    public void setUseCache(boolean useCache) {
        this.useCache = useCache;
    }

}
